// The NSpring Framework for .NET
// Copyright (c) 2003, Jeffrey Varszegi
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions are met:
// 
// - Redistributions of source code must retain the above copyright notice, this 
// list of conditions and the following disclaimer.
// 
// - Redistributions in binary form must reproduce the above copyright notice, 
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// 
// - Neither the name of the NSpring project nor the names of its contributors may
// be used to endorse or promote products derived from this software without 
// specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

namespace NSpring.Logging.Loggers {

using System;
using System.Messaging;
using System.Text;

using NSpring.Logging.Common.Pattern;
using NSpring.Logging.Common.Pattern.Patterns;
using NSpring.Logging.EventFormatters;

/// <summary>
/// <para>
/// A Logger implementation that writes messages to a message queue.
/// The default event formatter used is an instance of XMLEventFormatter
/// </para>
/// </summary>
public sealed class MessageQueueLogger : Logger {
    // Private$\\
    private const string BLANK_STRING                   = "";
    private const string PERIOD                         = ".";
    private const string ELLIPSIS                       = "...";    
    
    private string serverAddress;
    private string queueName;
    private string queuePath;
    
    private string summaryPatternSource;
    private SummaryPattern summaryPattern;
    
    private MessageQueue queue;
    
    private object queueMonitor                         = new object();
    
    private MessageQueueLogger() {}

    /// <summary>
    /// Creates a new MessageQueueLogger with the specified server address and
    /// queue name
    /// </summary>
    /// <param name="_serverAddress">The address of the server (PERIOD for the local machine)</param>
    /// <param name="_queueName">The name of the queue to be used by the logger</param>
    /// <param name="_summaryPattern">The pattern used to construct event summaries (set on the label of each message></param>
    public MessageQueueLogger(string _serverAddress, string _queueName, string _summaryPattern) {
        if ((_serverAddress == null) || (_queueName == null) || (_summaryPattern == null)) {
            throw new ArgumentNullException();
        }
        ServerAddress = _serverAddress;
        QueueName = _queueName;
        eventFormatter = new XMLEventFormatter();
        summaryPatternSource = _summaryPattern;
        summaryPattern = new SummaryPattern(summaryPatternSource);
        IsContextEnabled = true;
    }
    
    /// <summary>
    /// Creates a new MessageQueueLogger with the specified server address and
    /// queue name
    /// </summary>
    /// <param name="_serverAddress">The address of the server (PERIOD for the local machine)</param>
    /// <param name="_queueName">The name of the queue to be used by the logger</param>
    public MessageQueueLogger(string _serverAddress, string _queueName) {
        if ((_serverAddress == null) || (_queueName == null)) {
            throw new ArgumentNullException();
        }
        ServerAddress = _serverAddress;
        QueueName = _queueName;
        eventFormatter = new XMLEventFormatter();
        summaryPatternSource = "[{sn:uc}][{an:uc}][{ln:uc,1w}] {msg:30l}";
        summaryPattern = new SummaryPattern(summaryPatternSource);
        IsContextEnabled = true;
    }
    
    /// <summary>
    /// Creates a new MessageQueueLogger with the specified queue name; it will log to the
    /// machine hosting the process
    /// </summary>
    /// <param name="_queueName">The name of the queue to be used by the logger</param>
    public MessageQueueLogger(string _queueName) {
        if (_queueName == null) {
            throw new ArgumentNullException();
        }
        serverAddress = PERIOD;
        QueueName = _queueName;
        eventFormatter = new XMLEventFormatter();
        summaryPatternSource = "[{sn:uc}][{an:uc}][{ln:uc,1w}] {msg:30l}";
        summaryPattern = new SummaryPattern(summaryPatternSource);
        IsContextEnabled = true;
    }
    
    /// <summary>
    /// Gets and sets the address of the server hosting the underlying message queue
    /// </summary>
    public string ServerAddress {
        get {
            return serverAddress;
        }
        set {
            if (value == null) {
                value = PERIOD;
            }
            value = value.Trim();
            if (value.Equals(BLANK_STRING)) {
                throw new ArgumentException("Invalid value (must contain non-whitespace characters)");
            }
            lock (queueMonitor) {
                serverAddress = value;
                queuePath = serverAddress + '\\' + queueName;
                InitializeQueue();
            }
        }
    }
    
    /// <summary>
    /// Gets and sets the pattern string used to generate a summary line for
    /// each message; the default is "[{sn:uc}][{an:uc}][{ln:uc,1w}] {msg:30w}"
    /// </summary>
    public string SummaryPattern {
        get {
            return summaryPatternSource;
        }
        set {
            lock (monitor) {
                if (value == null) {
                    summaryPatternSource = null;
                    summaryPattern = null;
                }
                else {
                    SummaryPattern newSummaryPattern = new SummaryPattern(value);
                    summaryPatternSource = value;
                    summaryPattern = newSummaryPattern;
                }
            }
        }
    }
    
    /// <summary>
    /// Gets and sets the name of the underlying message queue
    /// </summary>
    public string QueueName {
        get {
            return queueName;
        }
        set {
            if (value == null) {
                throw new ArgumentNullException();
            }
            value = value.Trim();
            if (value.Equals(BLANK_STRING)) {
                throw new ArgumentException("Invalid value (must contain non-whitespace characters)");
            }
            lock (queueMonitor) {
                queueName = value;
                queuePath = serverAddress + '\\' + queueName;
                InitializeQueue();
            }
        }
    }
    
    private void InitializeQueue() {
        lock (queueMonitor) {
            if (queue != null) {
                try { queue.Close(); } catch {}
                queue = null;
            }
            try {
                MessageQueue.Create(queuePath);
            } catch {}
            try {
                queue = new MessageQueue(queuePath);
                queue.Formatter = new XmlMessageFormatter(new Type[] {typeof(string)});
            } catch (Exception e) {
                try { queue.Close(); } catch {}
                queue = null;
                throw new ApplicationException("Couldn't open queue at '" + queuePath + "': " + e.GetType().FullName + ": " + e.Message);
            }
        }
    }
    
    protected override void AcquireResources() {
        InitializeQueue();
    }
    
    protected override void ReleaseResources() {
        lock (queueMonitor) {
            if (queue != null) {
                try {
                    queue.Close();
                } catch {}
                queue = null;
            }
        }    
    }
    
    protected override void Write(Event _event) {
        if (_event == null) {
            return;
        }
        lock (queueMonitor) {
            if (queue == null) {
                InitializeQueue();
            }
            
            try {
                Message message = new Message();
                message.Body = eventFormatter.Format(_event);
                Pattern pattern = summaryPattern;
                if (pattern != null) {
                    message.Label = summaryPattern.Format(_event);
                }
                queue.Send(message);
            }
            catch (Exception e) {
                try { queue.Close(); } catch {}
                queue = null;
                throw new ApplicationException("Couldn't send message (" + e.GetType().FullName + ": " + e.Message + ")");
            }
            
        }
    }
    
}
}
